/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.alee.managers.glasspane;

import com.alee.laf.panel.WebPanel;
import com.alee.utils.LafUtils;
import com.alee.utils.SwingUtils;
import com.alee.utils.TextUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

/**
 * User: mgarin Date: 05.10.11 Time: 18:16
 */

public class WebGlassPane extends WebPanel
{
    // ID prefix
    private static final String ID_PREFIX = "WGP";

    // WebGlassPane id
    private String id = null;

    // Window to which WebGlassPane is attached
    private Window window = null;

    // WebGlassPane hit shape
    private Shape hitShape = null;

    // Additional painting
    private int imageOpacity = 0;
    private BufferedImage paintedImage = null;
    private Point imageLocation = null;

    // Window components highlight
    private int highlightBorder = 3;
    private Component highlightBase = WebGlassPane.this;
    private List<Component> highlightedComponents = new ArrayList<Component> ();

    public WebGlassPane ( Window window )
    {
        super ();

        this.window = window;

        setOpaque ( false );
        setFocusable ( false );
        setLayout ( null );
    }

    /**
     * WebGlassPane id
     */

    public String getId ()
    {
        if ( id == null )
        {
            id = TextUtils.generateId ( ID_PREFIX );
        }
        return id;
    }

    /**
     * Window to which WebGlassPane is attached
     */

    public Window getWindow ()
    {
        return window;
    }

    /**
     * Displays single component on WebGlassPane
     */

    public void showComponent ( JComponent component )
    {
        // Updating added component and its childs orientation
        SwingUtils.copyOrientation ( WebGlassPane.this, component );

        // Adding with 0 index to put component on top of all existing
        WebGlassPane.this.add ( component, 0 );
        WebGlassPane.this.revalidate ();
    }

    /**
     * WebGlassPane hit shape
     */

    public boolean contains ( int x, int y )
    {
        return hitShape != null && hitShape.contains ( x, y );
    }

    public Shape getHitShape ()
    {
        return hitShape;
    }

    public void setHitShape ( Shape hitShape )
    {
        this.hitShape = hitShape;
    }

    /**
     * WebGlassPane painted image
     */

    public int getImageOpacity ()
    {
        return imageOpacity;
    }

    public Point getImageLocation ()
    {
        return imageLocation;
    }

    public BufferedImage getPaintedImage ()
    {
        return paintedImage;
    }

    public void setPaintedImage ( BufferedImage paintedImage, Point imageLocation )
    {
        setPaintedImage ( 100, paintedImage, imageLocation );
    }

    public void setPaintedImage ( int imageOpacity, BufferedImage paintedImage, Point imageLocation )
    {
        Rectangle oldRect = getPaintedImageRect ();

        this.imageOpacity = imageOpacity;
        this.paintedImage = paintedImage;
        this.imageLocation = imageLocation;

        Rectangle repaintRect = null;
        if ( this.imageOpacity != 0 && this.paintedImage != null && this.imageLocation != null )
        {
            repaintRect = getPaintedImageRect ();
            if ( oldRect != null )
            {
                repaintRect = new Rectangle ( Math.max ( 0, Math.min ( oldRect.x, repaintRect.x ) ),
                        Math.max ( 0, Math.min ( oldRect.y, repaintRect.y ) ),
                        Math.max ( oldRect.x + oldRect.width, repaintRect.x + repaintRect.width ),
                        Math.max ( oldRect.y + oldRect.height, repaintRect.y + repaintRect.height ) );
            }
        }

        final Rectangle finalRepaintRect = repaintRect != null ? repaintRect : oldRect;
        SwingUtilities.invokeLater ( new Runnable ()
        {
            public void run ()
            {
                if ( finalRepaintRect != null )
                {
                    repaint ( finalRepaintRect );
                }
                else
                {
                    repaint ();
                }
            }
        } );
    }

    public void clearPaintedImage ()
    {
        final Rectangle oldRect = getPaintedImageRect ();

        this.imageOpacity = 0;
        this.paintedImage = null;
        this.imageLocation = null;

        if ( oldRect != null )
        {
            SwingUtilities.invokeLater ( new Runnable ()
            {
                public void run ()
                {
                    repaint ( oldRect );
                }
            } );
        }
    }

    public Rectangle getPaintedImageRect ()
    {
        Rectangle oldRect = null;
        if ( this.imageOpacity != 0 && this.paintedImage != null && this.imageLocation != null )
        {
            oldRect = new Rectangle ( this.imageLocation.x, this.imageLocation.y, this.paintedImage.getWidth (),
                    this.paintedImage.getHeight () );
        }
        return oldRect;
    }

    /**
     * WebGlassPane painted components highlight
     */

    public int getHighlightBorder ()
    {
        return highlightBorder;
    }

    public void setHighlightBorder ( int highlightBorder )
    {
        this.highlightBorder = highlightBorder;
    }

    public Component getHighlightBase ()
    {
        return highlightBase;
    }

    public void setHighlightBase ( Component highlightBase )
    {
        this.highlightBase = highlightBase;
    }

    public void addHighlightedComponent ( Component highlight )
    {
        if ( !highlightedComponents.contains ( highlight ) )
        {
            highlightedComponents.add ( highlight );
        }
        repaint ();
    }

    public void addHighlightedComponents ( List<Component> highlights )
    {
        for ( Component component : highlights )
        {
            if ( !highlightedComponents.contains ( component ) )
            {
                highlightedComponents.add ( component );
            }
        }
        repaint ();
    }

    public void removeHighlightedComponent ( Component highlight )
    {
        highlightedComponents.remove ( highlight );
        repaint ();
    }

    public void clearHighlights ()
    {
        highlightedComponents.clear ();
        repaint ();
    }

    /**
     * WebGlassPane painted focus change animation
     */

    //    public void displayFocusChange ( Component oldComponent, Component newComponent )
    //    {
    //        Rectangle oldBounds = SwingUtils.getRelativeBounds ( oldComponent, this );
    //        Rectangle newBounds = SwingUtils.getRelativeBounds ( newComponent, this );
    //
    //    }

    /**
     * WebGlassPane paint method
     */

    protected void paintComponent ( Graphics g )
    {
        super.paintComponent ( g );

        Graphics2D g2d = ( Graphics2D ) g;
        Object aa = LafUtils.setupAntialias ( g2d );

        if ( highlightedComponents.size () > 0 )
        {
            Rectangle baseBounds = SwingUtils.getRelativeBounds ( highlightBase, WebGlassPane.this );
            Area area = new Area ( new Rectangle ( baseBounds.x - 1, baseBounds.y - 1, baseBounds.width + 1, baseBounds.height + 1 ) );
            for ( Component component : highlightedComponents )
            {
                if ( component.isShowing () )
                {
                    Rectangle bounds = SwingUtils.getRelativeBounds ( component, WebGlassPane.this );
                    RoundRectangle2D.Double shape = new RoundRectangle2D.Double ( bounds.x - highlightBorder, bounds.y - highlightBorder,
                            bounds.width + highlightBorder * 2 - 1, bounds.height + highlightBorder * 2 - 1, 8, 8 );
                    area.subtract ( new Area ( shape ) );
                }
            }

            // Fill
            g2d.setPaint ( new Color ( 128, 128, 128, 128 ) );
            g2d.fill ( area );

            // Border
            g2d.setStroke ( new BasicStroke ( 1.5f ) );
            g2d.setPaint ( Color.GRAY );
            g2d.draw ( area );
        }

        if ( imageOpacity != 0 && paintedImage != null && imageLocation != null )
        {
            Composite c = g2d.getComposite ();
            if ( imageOpacity != 100 )
            {
                g2d.setComposite ( AlphaComposite.getInstance ( AlphaComposite.SRC_OVER, ( float ) imageOpacity / 100 ) );
            }

            g2d.drawImage ( paintedImage, imageLocation.x, imageLocation.y, null );

            if ( imageOpacity != 100 )
            {
                g2d.setComposite ( c );
            }
        }

        LafUtils.restoreAntialias ( g2d, aa );
    }
}
